package de.jadehs.cosima.ui.commands;

import java.io.IOException;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.ui.editor.XtextEditor;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;

import de.jadehs.cosima.Cosima;
import de.jadehs.cosima.CosimaUtil;
import de.jadehs.cosima.State;
import de.jadehs.cosima.Tape;
import de.jadehs.cosima.TransitionsTable;
import de.jadehs.cosima.ui.CosimaUIUtil;

public class RunMachineFromActiveEditor extends AbstractHandler implements
		IHandler {

	public Object execute(ExecutionEvent event) throws ExecutionException {
		IEditorPart editor = HandlerUtil.getActiveEditor(event);
		if (editor instanceof XtextEditor) {
			XtextEditor xed = (XtextEditor) editor;
			
			final IWorkbenchPage page = HandlerUtil.getActiveWorkbenchWindow(
					event).getActivePage();
			xed.getDocument().readOnly(new IUnitOfWork.Void<XtextResource>() {
				public void process(XtextResource resource) throws Exception {
					IParseResult parseResult = resource.getParseResult();
					final MessageConsole con = CosimaUIUtil
							.getCosimaConsole(page);
					Adapter machineLogListener = new AdapterImpl() {
						@Override
						public void notifyChanged(Notification msg) {
							log(con, msg);
						}
					};

					Cosima cosima = (Cosima) parseResult.getRootASTElement();
					handleAll(cosima, machineLogListener, true);
					cosima.eAdapters().add(machineLogListener);

					try {
						CosimaUtil.execute(cosima);
					} finally {
						handleAll(cosima, machineLogListener, false);
					}

					return;
				}
			});
		}
		return null;
	}

	private void handleAll(Cosima cosima, Adapter machineLogListener,
			boolean add) {
		TreeIterator<Object> iter = EcoreUtil.getAllContents(cosima, false);

		while (iter.hasNext()) {
			Object object = (Object) iter.next();
			if (object instanceof EObject) {
				if (add) {
					((EObject) object).eAdapters().add(machineLogListener);
				} else {
					((EObject) object).eAdapters().remove(machineLogListener);
				}
			}
		}
	}

	private void log(MessageConsole con, Notification msg) {
		final MessageConsoleStream out = con.newMessageStream();

		String text = describe(msg);

		if (text != null) {
			out.println(text);
		}

		try {
			out.close();
		} catch (IOException e) {
			;
		}
	}

	private String describe(Notification msg) {
		EObject eobj = (EObject) msg.getNotifier();

		if (eobj instanceof Tape) {
			if (msg.getEventType() == Notification.SET
					&& msg.getFeature() instanceof EAttribute
					&& ((EAttribute) msg.getFeature()).getName().equals(
							"position")) {
				return "moving tape from position "
						+ msg.getOldIntValue()
						+ " to "
						+ msg.getNewIntValue()
						+ " consuming input '"
						+ (msg.getNewIntValue() < ((Tape) eobj).getInput()
								.length() ? ((Tape) eobj).getInput().charAt(
								msg.getNewIntValue()) : "") + "'";
			}
		} else if (eobj instanceof TransitionsTable) {
			if (msg.getEventType() == Notification.SET
					&& msg.getFeature() instanceof EReference
					&& ((EReference) msg.getFeature()).getName().equals(
							"activeState")) {
				State state = (State) msg.getNewValue();
				return "active state changed: name=" + state.getName()
						+ ", type=" + state.getType();
			}
		}

		return null;
	}
}
